这是JS 原生方法原理探究系列的第五篇文章。本文会介绍如何实现 instanceof 方法。

typeof 操作符返回一个表示数据类型的字符串,它可以应付常规场景下的数据类型判断。对基本数据类型 undefinedbooleanstringnumberSymbol 和引用数据类型 function 都可以正确判断,但是对 null、数组、对象则统一返回 “object”。

比如说:

function F1(){}
function F2(){}
const obj1 = new F1()
const obj2 = new F2()
typeof obj1            // ‘object’
typeof obj2           // 'object' 

这里只能看出 obj1obj2 是对象,但不知道具体是哪个构造函数创建的对象。

但使用 instanceof 之后,就一目了然了:

console.log(obj1 instanceof F1)    // true
console.log(obj1 instanceof F2)    // false
console.log(obj2 instanceof F2)    // true

根据 MDN 的描述:

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

instanceof 运算符有两个操作数,左操作数通常是一个实例对象,它的类型可以是对象或者函数,也可以是基本类型(这种情况下不会报错,但总返回 false),右操作数通常是一个可调用的(callable)对象,我们可以直接认为它的类型应该是一个函数。

那么 instanceof 的实现原理是什么呢?从定义中我们可以看到,它的原理和原型链的机制有关,具体地说,它会拿到右操作数的原型对象,然后在左操作数上通过 __proto__ 不断查找实例的原型链,只要右操作数的 prototype 出现在左操作数的原型链上时,就返回 true。如果原型链一直查找到尽头 —— 也就是 null,还没有找到右操作数的原型,就返回 false

所以,在模拟实现中,我们只要不断遍历左操作数的原型链,取得原型链上的原型对象,并与右操作数的原型对象比较即可。

下面是具体的代码实现:

function myInstanceof(instance,constructor){
    if(typeof instance != 'object' && typeof instance != 'function' || instance == null){
        return false
    }
    if(typeof constructor != 'function'){
        throw TypeError('the right-hand-side of instanceof must be a function')
    }
    let proto = constructor.prototype
    let p = instance.__proto__
    while(p != null){
        if(p == proto){
            return true
        }
        p = p.__proto__
    }
}